package com.brayan.webapp.service.realestate;

import java.util.ArrayList;
import java.util.List;

import javax.ejb.Stateless;
import javax.inject.Inject;

import org.joda.time.LocalDate;

import com.brayan.webapp.model.realestate.pricemodel.WeekFactor;
import com.brayan.webapp.repository.realestate.ReservationRepository;

/**
 * This bean knows about special days like Eastern or Ascension Day and provides
 * a factor to multiply with the expense. Also, it assigns weeks in specific
 * seasons a specific multiplication factor.
 * <p/>
 * Factor always equals or greater than s1. So prices are set to the maximum.
 * And discounts are provided.
 * 
 * @author Brayan
 * 
 */
@Stateless
public class SeasonFactorBean {

	private static final double DEFAULT_SEASON_FACTOR = 1;

	public SeasonFactorBean() {
	}

	@Inject
	ReservationRepository resRepo;

	private LocalDate startDate;
	private LocalDate endDate;
	private int startWeek;
	private int endWeek;

	public LocalDate getStartDate() {
		return startDate;
	}

	public void setStartDate(LocalDate startDate) {
		this.startDate = startDate;
	}

	public LocalDate getEndDate() {
		return endDate;
	}

	public void setEndDate(LocalDate endDate) {
		this.endDate = endDate;
	}

	/**
	 * This function calculates an amount that is either negative or positive on
	 * basis of the provided total amount and the given start and end date.
	 * <p/>
	 * It checks weeks and days. If a week contains an expensive day, the entire
	 * week is increased to this factor.
	 * 
	 * @param startDate
	 * @param endDate
	 * @param total
	 * @return
	 */
	public double calculateSeasonFactorAmount(LocalDate startDate, LocalDate endDate, double total) {

		// Find the weeks that the interval is in.
		List<WeekFactor> listOfWeekFactors = getListOfWeekFactors(startDate, endDate);
		double averageFactor = calculateAverageFactor(listOfWeekFactors);
		double newTotal = total * averageFactor;
		double difference = newTotal - total;
		return difference;
	}

	/**
	 * Return the weeks which are touched by the start and end date.
	 * 
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	private List<WeekFactor> getListOfWeekFactors(LocalDate startDate, LocalDate endDate) {
		this.startDate = startDate;
		this.endDate = endDate;
		startWeek = startDate.getWeekOfWeekyear();
		endWeek = endDate.getWeekOfWeekyear();

		List<Integer> weeks = new ArrayList<Integer>();
		if (endWeek < startWeek) {
			for (int i = startWeek; i <= 52; i++)
				weeks.add(i);
			for (int i = 1; i <= endWeek; i++)
				weeks.add(i);
		} else {
			for (int i = startWeek; i <= endWeek; i++)
				weeks.add(i);
		}
		List<WeekFactor> listOfWeeksFactors = resRepo.getWeeks(weeks);
		// If there is no week, then set a default week.
		if (listOfWeeksFactors.isEmpty()) {
			// TODO log this fact
			WeekFactor defaultWeekFactor = new WeekFactor();
			defaultWeekFactor.setWeekNumber(0);
			defaultWeekFactor.setFixedTimeInterval(true);
			defaultWeekFactor.setDiscountFactor(DEFAULT_SEASON_FACTOR);
			defaultWeekFactor.setNameOfFactorUnit("Standard");
			listOfWeeksFactors.add(defaultWeekFactor);
		}
		return listOfWeeksFactors;
	}

	public double calculateAverageFactor(List<WeekFactor> weekFactors) {
		Double sum = 0.0;
		if (!weekFactors.isEmpty()) {
			for (WeekFactor weekFactor : weekFactors) {
				sum += weekFactor.getDiscountFactor();
			}
			return sum.doubleValue() / weekFactors.size();
		}
		return sum;
	}

	public double calculateAverageFactor(LocalDate startDate, LocalDate endDate) {

		return calculateAverageFactor(getListOfWeekFactors(startDate, endDate));
	}
}
